#include "Sizeable.h"
#include <QMouseEvent>
#include <QTimerEvent>
#include <QCursor>

Sizeable::Sizeable(QWidget *parent)
    : QObject(parent)
    , _isEnabled(true)
{
    _sizeableWidget = parent;
    _borderWidth = 0;

    _isPressed = false;
    _regionPressed = UNKNOWN;
    _cursorTimerHandler = 0;

    if (_sizeableWidget)
    {
        _sizeableWidget->setMouseTracking(true);
        _sizeableWidget->installEventFilter(this);    // 代理窗体事件

        //模拟一次鼠标事件, 避免界面第一次缩放时出现的比例错误的问题;
        QPoint pos(1, 1);
        QMouseEvent pressEvent = QMouseEvent(QEvent::MouseButtonPress, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
        QMouseEvent releaseEvent = QMouseEvent(QEvent::MouseButtonRelease, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
        QApplication::sendEvent(_sizeableWidget, &pressEvent);
        QApplication::sendEvent(_sizeableWidget, &releaseEvent);

        QObjectList objs = childrenWidgets(_sizeableWidget);
        for(QObject *pObj : objs)
        {
            if (pObj->isWidgetType())
                qobject_cast<QWidget*>(pObj)->setMouseTracking(true);
        }
        QMouseEvent mouseMoveEvent = QMouseEvent(QEvent::MouseMove, pos, Qt::NoButton, Qt::NoButton, Qt::NoModifier);
        QApplication::sendEvent(_sizeableWidget, &mouseMoveEvent);
    }
}
Sizeable::~Sizeable()
{}

void Sizeable::setBorderWidth(uint width)
{
    _borderWidth = width;
    makeRegions();
}
void Sizeable::setEnabled(bool kEnabled)
{
    _isEnabled = kEnabled;
}

void Sizeable::updateGeometry(int x, int y, int w, int h)
{
    int minWidth = _sizeableWidget->minimumWidth();
    int minHeight = _sizeableWidget->minimumHeight();
    int maxWidth = _sizeableWidget->maximumWidth();
    int maxHeight = _sizeableWidget->maximumHeight();

    if (w < minWidth || w > maxWidth || h < minHeight || h > maxHeight)
       return;

    _sizeableWidget->setGeometry(x, y, w, h);
}

bool Sizeable::eventFilter(QObject* obj, QEvent* event)
{
    if (!_isEnabled)
        return QObject::eventFilter(obj, event);

    QEvent::Type eventType = event->type();
    if (QEvent::MouseMove == eventType)
    {
        QMouseEvent* mouseEvent = (QMouseEvent*)event;
        QPoint currPosLocal = mouseEvent->pos();
        QPoint currPosGlobal = _sizeableWidget->mapToGlobal(currPosLocal);

        if (!_isPressed) // 鼠标未按下
        {
            Sizeable::WidgetRegion regionType = findRegion(currPosLocal);
            switch (regionType)
            {
            case TOP:
            case BOTTOM:
                _sizeableWidget->setCursor(Qt::SizeVerCursor);
                break;
            case RIGHT_TOP:
            case LEFT_BOTTOM:
                _sizeableWidget->setCursor(Qt::SizeBDiagCursor);
                break;
            case RIGHT:
            case LEFT:
                _sizeableWidget->setCursor(Qt::SizeHorCursor);
                break;
            case RIGHT_BOTTOM:
            case LEFT_TOP:
                _sizeableWidget->setCursor(Qt::SizeFDiagCursor);
                break;
            default:
                _sizeableWidget->setCursor(Qt::ArrowCursor);
                break;
            }
            startCursorTimer();
        }
        else // 鼠标已按下
        {
            if (TOP == _regionPressed)
            {
                int dY = currPosGlobal.y() - _originPosGlobal.y();
                updateGeometry(_originGeometry.x(), _originGeometry.y() + dY, _originGeometry.width(), _originGeometry.height() - dY);
            }
            else if (RIGHT_TOP == _regionPressed)
            {
                QPoint dXY = currPosGlobal - _originPosGlobal;
                updateGeometry(_originGeometry.x(), _originGeometry.y() + dXY.y(), _originGeometry.width() + dXY.x(), _originGeometry.height() - dXY.y());
            }
            else if (RIGHT == _regionPressed)
            {
                int dX = currPosGlobal.x() - _originPosGlobal.x();
                updateGeometry(_originGeometry.x(), _originGeometry.y(), _originGeometry.width() + dX, _originGeometry.height());
            }
            else if (RIGHT_BOTTOM == _regionPressed)
            {
                QPoint dXY = currPosGlobal - _originPosGlobal;
                updateGeometry(_originGeometry.x(), _originGeometry.y(), _originGeometry.width() + dXY.x(), _originGeometry.height() + dXY.y());
            }
            else if (BOTTOM == _regionPressed)
            {
                int dY = currPosGlobal.y() - _originPosGlobal.y();
                updateGeometry(_originGeometry.x(), _originGeometry.y(), _originGeometry.width(), _originGeometry.height() + dY);
            }
            else if (LEFT_BOTTOM == _regionPressed)
            {
                QPoint dXY = currPosGlobal - _originPosGlobal;
                updateGeometry(_originGeometry.x() + dXY.x(), _originGeometry.y(), _originGeometry.width() - dXY.x(), _originGeometry.height() + dXY.y());
            }
            else if (LEFT == _regionPressed)
            {
                int dX = currPosGlobal.x() - _originPosGlobal.x();
                updateGeometry(_originGeometry.x() + dX, _originGeometry.y(), _originGeometry.width() - dX, _originGeometry.height());
            }
            else if (LEFT_TOP == _regionPressed)
            {
                QPoint dXY = currPosGlobal - _originPosGlobal;
                updateGeometry(_originGeometry.x() + dXY.x(), _originGeometry.y() + dXY.y(), _originGeometry.width() - dXY.x(), _originGeometry.height() - dXY.y());
            }
        }
    }
    else if (QEvent::MouseButtonPress == eventType)
    {
        QMouseEvent* mouseEvent = (QMouseEvent*)event;
        if (mouseEvent->button() == Qt::LeftButton)
        {
            _isPressed = true;

            QPoint currPos = mouseEvent->pos();
            _regionPressed = findRegion(currPos);

            _originPosGlobal = _sizeableWidget->mapToGlobal(currPos);
            _originGeometry = _sizeableWidget->geometry();

            stopCursorTimer();
        }
    }
    else if (QEvent::MouseButtonRelease == eventType)
    {
        _isPressed = false;
        _regionPressed = UNKNOWN;

        _sizeableWidget->setCursor(Qt::ArrowCursor);
    }
    else if (QEvent::Resize == eventType)
    {
        makeRegions(); 
    }
    else if (QEvent::Leave == eventType)
    {
        _sizeableWidget->setCursor(Qt::ArrowCursor);
        stopCursorTimer();
    }
    else if (QEvent::Timer == eventType)
    {
        QTimerEvent* timerEvent = (QTimerEvent*)event;
        if (timerEvent->timerId() == _cursorTimerHandler)
        {
            if (_regions[INNER].contains(_sizeableWidget->mapFromGlobal(QCursor::pos())))
            {
                _sizeableWidget->setCursor(Qt::ArrowCursor);
                stopCursorTimer();
            }
        }
    }

    return QObject::eventFilter(obj, event);
}

void Sizeable::startCursorTimer()
{
    stopCursorTimer();
    _cursorTimerHandler = _sizeableWidget->startTimer(50);
}
void Sizeable::stopCursorTimer()
{
    if (0 != _cursorTimerHandler)
    {
        _sizeableWidget->killTimer(_cursorTimerHandler);
        _cursorTimerHandler = 0;
    }
}

QObjectList Sizeable::childrenWidgets(QWidget *pWidget)
{
    QObjectList l;
    QObjectList list = pWidget->children();
    for (QObject *pObj : list)
    {
        if (pObj->isWidgetType())
        {
            l << pObj;
            if (!pObj->children().isEmpty())
                l << childrenWidgets(qobject_cast<QWidget*>(pObj));
        }
    }
    return l;
}

void Sizeable::makeRegions()
{
    int width = _sizeableWidget->width();
    int height = _sizeableWidget->height();

#if 0
    _regions[LEFT_TOP]      = QRect(0, 0, _borderWidth * 2, _borderWidth * 2);
    _regions[TOP]           = QRect(_borderWidth * 2, 0, width - _borderWidth * 4, _borderWidth*2);
    _regions[RIGHT_TOP]     = QRect(width - _borderWidth * 2, 0, _borderWidth * 2, _borderWidth * 2);
    _regions[RIGHT]         = QRect(width - _borderWidth, _borderWidth * 2, _borderWidth*2, height - _borderWidth * 4);
    _regions[RIGHT_BOTTOM]  = QRect(width - _borderWidth * 2, height - _borderWidth * 2, _borderWidth * 2, _borderWidth * 2);
    _regions[BOTTOM]        = QRect(_borderWidth * 2, height - _borderWidth, width - _borderWidth * 4, _borderWidth*2);
    _regions[LEFT_BOTTOM]   = QRect(0, height - _borderWidth * 2, _borderWidth * 2, _borderWidth * 2);
    _regions[LEFT]          = QRect(0, _borderWidth * 2, _borderWidth*2, height - _borderWidth * 4);

    _regions[INNER]         = QRect(_borderWidth * 2, _borderWidth * 2, width - _borderWidth * 4, height - _borderWidth * 4);
#else
    _regions[LEFT_TOP]      = QRect(0, 0, _borderWidth, _borderWidth);
    _regions[TOP]           = QRect(_borderWidth, 0, width - _borderWidth * 2, _borderWidth);
    _regions[RIGHT_TOP]     = QRect(width - _borderWidth, 0, _borderWidth, _borderWidth);
    _regions[RIGHT]         = QRect(width - _borderWidth, _borderWidth, _borderWidth, height-_borderWidth*2);
    _regions[RIGHT_BOTTOM]  = QRect(width - _borderWidth, height - _borderWidth, _borderWidth, _borderWidth);
    _regions[BOTTOM]        = QRect(_borderWidth, height - _borderWidth, width-_borderWidth*2, _borderWidth);
    _regions[LEFT_BOTTOM]   = QRect(0, height-_borderWidth, _borderWidth, _borderWidth);
    _regions[LEFT]          = QRect(0, _borderWidth, _borderWidth, height-_borderWidth*2);

    _regions[INNER]         = QRect(_borderWidth, _borderWidth, width - _borderWidth * 2, height - _borderWidth * 2);

#endif
}

Sizeable::WidgetRegion Sizeable::findRegion(const QPoint& pos)
{
    for (int i = 0; i < 9; i++)
    {
        const QRect rect = _regions[i];
        if (rect.contains(pos))
        {
            return Sizeable::WidgetRegion(i);
        }
    }
    return UNKNOWN;
}
